﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Xml.Serialization;
using Microsoft.WowAddonStudio.FrameXml.Serialization.TypeConverters;
using Microsoft.WowAddonStudio.FrameXml.Serialization.Attributes;

namespace Microsoft.WowAddonStudio.FrameXml.Serialization.LayoutFrames
{
    [Serializable]
    [DesignerCategory("code")]
    [XmlType(Namespace = "http://www.blizzard.com/wow/ui/")]
    [XmlRoot(Namespace = "http://www.blizzard.com/wow/ui/", IsNullable = false)]
    [TypeConverter(typeof (LayoutFrameTypeConverter))]
    public class LayoutFrame : InheritableObject, ICloneable, ICopiable<LayoutFrame>
    {
        private bool? layoutHidden;

        /// <summary>
        /// Gets or sets a value indicating whether all points should be automaticall set.
        /// </summary>
        [XmlAttribute("setAllPoints")]
        [DefaultValue(false)]
        [Category("Behavior")]
        [Description("Attaches all four corners to the parent's inner corners.")]
        public bool SetAllPoints
        {
            get { return Properties.GetValue<bool>("SetAllPoints"); }
            set { Properties.SetValue("SetAllPoints", value); }
        }

        /// <summary>
        /// Gets or set a value indicating whether the <see cref="LayoutFrame"/> is hidden.
        /// </summary>
        [XmlAttribute("hidden")]
        [DefaultValue(false)]
        [Category("Layout")]
        [Description("If true element is skipped in drawing resulting it not appearing on screen (interactions also skipped). Default false.")]
        public bool Hidden
        {
            get { return Properties.GetValue<bool>("Hidden"); }
            set { Properties.SetValue("Hidden", value); }
        }

        /// <summary>
        /// Gets or sets the size of the layout frame.
        /// </summary>
        [XmlElement("Size")]
        [Category("Layout")]
        [DisplayName("Size")]
        [Description("Defines a size of an element. Normally this is done by putting \"AbsDimension\" tag inside with \"x\" and \"y\" attributies, but sometimes this is done by just giving \"x\" and \"y\" attributies to \"Size\" tag itself.")]
        public Dimension SizeDimension
        {
            get { return Properties.GetValue<Dimension>("SizeDimension"); }
            set { Properties.SetValue("SizeDimension", value); }
        }

        /// <summary>
        /// Gets or sets the anchors of the layout frame.
        /// </summary>
        [XmlArray]
        [XmlArrayItem("Anchor")]
        [Category("Layout")]
        [Description("This defines position of element relative to it's parent position. You can define one or more \"anchor\" tags within \"anchors\".")]
        [AnchorAttribute]
        public Anchor[] Anchors
        {
            get { return Properties.GetValue<Anchor[]>("Anchors"); }
            set { Properties.SetValue("Anchors", value); }
        }

        /// <summary>
        /// Gets or sets the parent layout frame.
        /// </summary>
        [XmlIgnore]
        [Browsable(false)]
        public LayoutFrame ParentFrame { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this <see cref="LayoutFrame"/> is locked.
        /// </summary>
        /// <value><c>true</c> if locked; otherwise, <c>false</c>.</value>
        [XmlIgnore]
        [Browsable(false)]
        public bool Locked { get; set; }
        
        /// <summary>
        /// Gets or sets a value indicating whether [layout hidden].
        /// </summary>
        /// <value><c>true</c> if [layout hidden]; otherwise, <c>false</c>.</value>
        [XmlIgnore]
        [Browsable(false)]
        public bool LayoutHidden
        {
            get
            {
                // If there is no layout setted use the default value.
                if (!layoutHidden.HasValue)
                    layoutHidden = Hidden;

                return layoutHidden.Value;
            }
            set
            {
                layoutHidden = value;

                OnPropertyChanged(new PropertyChangedEventArgs("LayoutHidden"));
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is in virtual state.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is in virtual state; otherwise, <c>false</c>.
        /// </value>
        [XmlIgnore]
        [Browsable(false)]
        public bool IsInVirtualState { get; set; }

        #region ICloneable Members

        /// <summary>
        /// Creates a new object that is a copy of the current instance.
        /// </summary>
        /// <returns>
        /// A new object that is a copy of this instance.
        /// </returns>
        public object Clone()
        {
            return Copy();
        }

        #endregion

        #region ICopiable<LayoutFrame> Members

        /// <summary>
        /// Creates a new object that is a copy of the instance.
        /// </summary>
        /// <returns>The copied instance.</returns>
        /// <remarks>Performs a deep copy.</remarks>
        public LayoutFrame Copy()
        {
            var layoutFrame = new LayoutFrame
                                  {
                                      SetAllPoints = SetAllPoints,
                                      Hidden = Hidden,
                                      SizeDimension = SizeDimension != null ? SizeDimension.Copy() : null,
                                  };

            if (Anchors != null && Anchors.Length > 0)
            {
                var anchorArray = new Anchor[Anchors.Length];

                for (int i = 0; i < Anchors.Length; i++)
                {
                    anchorArray[i] = Anchors[i].Copy();
                }

                layoutFrame.Anchors = anchorArray;
            }

            return layoutFrame;
        }

        #endregion

        /// <summary>
        /// Initializes the parent of the layout frame.
        /// </summary>
        /// <param name="parentFrame">The parent fame of the layout frame.</param>
        /// <param name="deep">True, if the parent's of all children should be initialized.</param>
        public void InitializeParent(LayoutFrame parentFrame, bool deep)
        {
            ParentFrame = parentFrame;

            if (deep)
            {
                var container = this as ISerializationObjectContainer;
                if (container != null)
                {
                    // Iterate through the child frames, excluding the inherited ones (as they have their own parent)
                    foreach (LayoutFrame childFrame in container.GetChildren(false).OfType<LayoutFrame>())
                        childFrame.InitializeParent(this, true);
                }
            }
        }

        /// <summary>
        /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
        /// </returns>
        public override string ToString()
        {
            return Name;
        }
    }
}